﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using StockImport.dataimport.common;
using StockImport.http;
using StockCommon.data;
using System.IO;
using StockCommon.Calendar;
using HAP = HtmlAgilityPack;
using StockCommon;
using StockCommon.TaskLog;
using Utilities;
using System.Text.RegularExpressions;

namespace StockImport.dataimport.otc.dailyindex
{
    public class OTC_DailyIndexImport : DataModuleCommon
    {
        protected Guid taskId;
        OTC_DailyIndexFetcher fetcher = new OTC_DailyIndexFetcher();

        public override Guid getTaskId()
        {
            return taskId;
        }
        protected override HttpContentFetcher_Daily getFether()
        {
            return fetcher;
        }


        public OTC_DailyIndexImport()
        {
            taskId = ObjectExecServices.getServicesId("sitefetcher", getTaskName(), getVersion());
        }

        public override DateTime getStartDate()
        {
            return DateTime.Parse("2012/05/29");
        }

        public override DateTime getEndDate()
        {
            return DateTime.Parse("2014/01/01");
        }

        public override String getTaskName()
        {
            return "上櫃_股票行情 ";
        }

        public override String getVersion()
        {
            return "2.0";
        }


        public override bool hasSourceData(DateTime date)
        {
            String fileName = OTC_DailyIndexFetcher.getStoreFileName(date);
            if (File.Exists(fileName) == false)
            {
                return false;
            }
            return true;
        }
        protected override ProcessResultEnum UpdateShell(DateTime date, bool force, ref UpdateResult result)
        {
            String fileName = OTC_DailyIndexFetcher.getStoreFileName(date);
            String html = System.IO.File.ReadAllText(fileName, Encoding.GetEncoding("utf-8"));
            ProcessResultEnum state = processSource(date, html, force, ref result);
            return state;
        }

        private ProcessResultEnum processSource(DateTime date, String Utf8html, bool force, ref UpdateResult result)
        {
            try
            {
                HAP.HtmlDocument doc = new HAP.HtmlDocument();
                doc.LoadHtml(Utf8html);
                DateTime realDate = resolveRealDate(doc);
                //沒有這種日期~基本上不會有這種事~
                if (realDate.CompareTo(date) != 0)
                    return ProcessResultEnum.發生錯誤;

                //找出是否有...如果有-->Transaction 
                int c = StockDailyIndexServices.getRecordCount(date, TwnStockMarketType.上櫃);

                if ((c > 0) && (force == false))
                {
                    result.result = true;
                    result.Message = "資料庫已有資料:" + c + "筆";
                    return ProcessResultEnum.完成且有資料;
                }
                if (c > 0)
                    StockDailyIndexServices.deleteRecord(date, TwnStockMarketType.上櫃);

                List<OtcDailyEntry> entries = HtmlToEntries(doc);

                foreach (OtcDailyEntry e in entries)
                {
                    Guid id = StockCodeManager.getGreTaiId(e.代號);
                     if (Guid.Empty.Equals(id))
                        {
                            //因為當載入了有憑證的檔案,資料比數會超大,
                            //當找不到的時候,就只有len<4的可以產生新的,不過這應該也用步道                            
                            if ((entries.Count < 1000)&&(e.代號.Length<=4))
                            {
                                id = StockCodeManager.createStock(e.代號, e.名稱, "otc", DateTime.Now);
                            }
                        }
                    e.id = id;
                }



                if (entries.Count == 0)
                    return ProcessResultEnum.必須重做;

                StockDataClassesDataContext context = LinqAgent.getContext();
                foreach (OtcDailyEntry e in entries)
                {
                    if (e.id.Equals(Guid.Empty))
                        continue;

                    StockDaily sd = new StockDaily();
                    sd.日期 = realDate;
                    sd.證券代號 = e.代號;
                    sd.開盤價 = e.開盤;
                    sd.最高價 = e.最高;
                    sd.最低價 = e.最低;
                    sd.收盤價 = e.收盤;
                    sd.成交股數 = e.成交股數;
                    sd.成交金額 = e.成交金額;
                    sd.成交筆數 = e.成交筆數;
                    sd.漲跌價差 = e.漲跌;
                    context.StockDailies.InsertOnSubmit(sd);
                }
                context.SubmitChanges();
                StockEventTags stockEventTags = new StockEventTags();
                foreach (OtcDailyEntry e in entries)
                {
                    if (e.id.Equals(Guid.Empty))
                        continue;

                    if (e.tagSet.Count == 0)
                        continue;
                    if (e.tagSet.Contains(StatusEnum.CashDividen))
                        stockEventTags.setCashDividend(e.代號, realDate);
                    if (e.tagSet.Contains(StatusEnum.StockDividen))
                        stockEventTags.setStockDividend(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Up_Start))
                        stockEventTags.setLimitUP_Start(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Up_End))
                        stockEventTags.setLimitUP_End(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Up_Max))
                        stockEventTags.setLimitUP_Max(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Up_Min))
                        stockEventTags.setLimitUP_Min(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Down_Start))
                        stockEventTags.setLimitDOWN_Start(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Down_End))
                        stockEventTags.setLimitDOWN_End(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Down_Max))
                        stockEventTags.setLimitDOWN_Max(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Down_Min))
                        stockEventTags.setLimitDOWN_Min(e.代號, realDate);
                }
                stockEventTags.flush();
                result.result = true;
                result.Message = "更新資料:" + entries.Count + "筆";
                System.Diagnostics.Debug.WriteLine(result.Message);
                return ProcessResultEnum.完成且有資料;
            }
            catch (Exception e)
            {
                result.result = false;
                result.Message = e.Message;
                System.Diagnostics.Debug.WriteLine(e.StackTrace);
                return ProcessResultEnum.發生錯誤;
            }

        }


        private List<OtcDailyEntry> HtmlToEntries(HAP.HtmlDocument doc)
        {
            HAP.HtmlNode table = doc.DocumentNode.SelectSingleNode("//table[@id='contentTable']");
            HAP.HtmlNodeCollection ns = table.SelectNodes(table.XPath + "/tr");
            List<OtcDailyEntry> entries = new List<OtcDailyEntry>();
            foreach (HAP.HtmlNode row in ns)
            {
                HAP.HtmlNodeCollection tds = row.SelectNodes(row.XPath + "/td");
                if (tds.Count != 15)
                    continue;
                OtcDailyEntry entry = processRow(tds);
                entries.Add(entry);
            }
            return entries;
        }

        private OtcDailyEntry processRow(HAP.HtmlNodeCollection tds)
        {
            OtcDailyEntry entry = new OtcDailyEntry();
            //0 =>代號
            decimal decvalue = 0;

            entry.代號 = tds[0].InnerText.Trim();
            //1名稱	
            entry.名稱 = tds[1].InnerText.Trim();
            //2收盤 	

            if (processDecimal(tds[2].InnerText, ref decvalue))
            {
                entry.收盤 = decvalue;
            }
            checkLimit(entry, tds[2].InnerText, StatusEnum.Up_End, StatusEnum.Down_End);
            //3漲跌	
            if (processDecimal(tds[3].InnerText, ref decvalue))
            {
                entry.漲跌 = decvalue;
            }
            checkDividen(entry, tds[3].InnerText);
            //4開盤 
            if (processDecimal(tds[4].InnerText, ref decvalue))
            {
                entry.開盤 = decvalue;
            }
            checkLimit(entry, tds[4].InnerText, StatusEnum.Up_Start, StatusEnum.Down_Start);
            //5最高 
            if (processDecimal(tds[5].InnerText, ref decvalue))
            {
                entry.最高 = decvalue;
            }
            checkLimit(entry, tds[5].InnerText, StatusEnum.Up_Max, StatusEnum.Down_Max);
            //6最低	
            if (processDecimal(tds[6].InnerText, ref decvalue))
            {
                entry.最低 = decvalue;
            }
            checkLimit(entry, tds[6].InnerText, StatusEnum.Up_Min, StatusEnum.Down_Min);
            //7成交股數 	
            if (processDecimal(tds[7].InnerText, ref decvalue))
            {
                entry.成交股數 = Convert.ToInt32(decvalue);
            }
            //8成交金額(元)	
            if (processDecimal(tds[8].InnerText, ref decvalue))
            {
                entry.成交金額 = decvalue;
            }
            //9	成交筆數
            if (processDecimal(tds[9].InnerText, ref decvalue))
            {
                entry.成交筆數 = Convert.ToInt32(decvalue);
            }
            //10最後買價
            //11最後賣價
            //12發行股數
            if (processDecimal(tds[12].InnerText, ref decvalue))
            {
                entry.發行股數 = Convert.ToInt64(decvalue);
            }
            //13次日漲停價
            //14次日跌停價
            return entry;
        }
        protected void checkDividen(OtcDailyEntry entry, String txt)
        {
            if (txt.Contains("除權"))
                entry.tagSet.Add(StatusEnum.StockDividen);
            if (txt.Contains("除權息"))
                entry.tagSet.Add(StatusEnum.CashDividen);
            if (txt.Contains("除息"))
                entry.tagSet.Add(StatusEnum.CashDividen);
        }
        protected void checkLimit(OtcDailyEntry entry, String txt, StatusEnum s1, StatusEnum s2)
        {
            if (txt.Contains("⊕"))
                entry.tagSet.Add(s1);
            if (txt.Contains("⊙"))
                entry.tagSet.Add(s2);
            if (txt.Contains("----"))
                entry.tagSet.Add(StatusEnum.Stop);


        }
        private static bool processDecimal(string text, ref decimal value)
        {
            text = text.Trim();
            decimal localdec;
            String[] ss = text.Split(' ');

            if (ss.Length == 1)
            {
                if (decimal.TryParse(ss[0], out localdec))
                {
                    value = localdec;
                    return true;
                }
                return false;
            }
            if (ss[0].Contains("+"))
            {
                if (decimal.TryParse(ss[1], out localdec))
                {
                    value = localdec;
                    return true;
                }
                return false;

            }
            if (ss[0].Contains("-"))
            {
                if (decimal.TryParse(ss[1], out localdec))
                {
                    value = 0 - localdec;
                    return true;
                }
                return false;
            }
            if (decimal.TryParse(ss[1], out localdec))
            {
                value = localdec;
                return true;
            }
            return false;
        }



        private DateTime resolveRealDate(HAP.HtmlDocument doc)
        {
            HAP.HtmlNodeCollection ns = doc.DocumentNode.SelectNodes("//td[@class='table-title']");
            if (ns == null)
                throw new Exception("No Date Entry");
            HAP.HtmlNode n = ns[0];
            String t = n.InnerText;
            Regex titleRegx = new Regex(@"(\d+)年(\d+)月(\d+)日", RegexOptions.IgnoreCase);
            Match match = titleRegx.Match(t);
            if (match.Success == false)
                throw new Exception("No Date Entry");
            int yy, mm, dd;
            yy = int.Parse(match.Groups[1].Value);
            mm = int.Parse(match.Groups[2].Value);
            dd = int.Parse(match.Groups[3].Value);

            yy = 1911 + yy;
            DateTime realDate = DateTime.Parse(String.Format("{0}/{1}/{2}", yy, mm, dd));
            return realDate;
        }
    }
    public class OtcDailyEntry
    {
        public String 代號;
        public String 名稱;
        public Guid id;
        public decimal 收盤;
        public decimal 漲跌;
        public decimal 開盤;
        public decimal 最高;
        public decimal 最低;
        public int 成交股數;
        public decimal 成交金額;
        public int 成交筆數;
        public int 漲跌狀態 = 0; // =1 長 =2 漲停  =-1 跌 -2 跌停  
        public long 發行股數;
        public bool isManaged = false;

        public HashSet<StatusEnum> tagSet = new HashSet<StatusEnum>();
    }
    public enum StatusEnum : int
    {
        Stop = 1,
        Up_Start = 10,
        Up_End = 11,
        Up_Max = 12,
        Up_Min = 13,

        Down_Start = 21,
        Down_End = 22,
        Down_Max = 23,
        Down_Min = 24,

        StockDividen = 51,
        CashDividen = 52
    }
}
